/*
 * Tree.java    (28.jul.07)
 *
 * @author      Lobsang Alvites Szaler (l.alvites@lpcsite.com)
 * @version     0.40
 *
 * Copyright (c)2007 Soluciones Info Móviles SAC
 *
 */

package sim.utils.tree;

import java.io.UnsupportedEncodingException;
import java.util.*;

import sim.framework.io.*;
import sim.framework.*;
import sim.utils.*;
import sim.utils.parser.*;

// FILE TREE STRUCTURE
// 1a (0)
// 2a (0.1)
// 2b (0.1)
// 2c (0.1)
// 3a (0.1.2)
// 3b (0.1.2)
// 1b (0)
// 1c (0)
// 2a (0.2)
// 2b (0.2)
// 2c (0.2)
// 1d (0)
//
// (Tree_s)SHELL_ROOT
// +-----------------+
// !m_parent = null  !
// !vec m_subtrees  -+--+------+
// +-----------------+  ! 1a  -+-----------------+------+
//                      ! 1b   !                 ! 2a   !
//                      ! 1c  -+-----+------+    ! 2b   !
//                      ! 1d   !     ! 2a   !    ! 2c  -+----+------+
//                      +------+     ! 2b   !    +------+    ! 3a   !
//                                   ! 2c   !                ! 3b   !
//                                   +------+                +------+

public abstract class Tree implements ParserScan, FileGets {
    
    private Simlet  m_Simlet;       // [RESERVED] reference to Simlet
    private Tree_s  m_TreeS;        // [RESERVED] Tree structure to be filled
    private int     m_CPOS;         // [RESERVED] last menu level
    private Parser  m_PT;           // [RESERVED] Parser object
    private int     m_pos;          // [RESERVED] get value (remembers Tree structure level)

    private String  m_strName;      // [temp] get value
    private int     m_Content;
    private int     m_contOfst;     // [temp] Content data (offset)
    private int     m_contLen;      // [temp] Content data (length)
    
    
    /*
     *  Constructor
     */
    public Tree( final Simlet s ) {
        m_Simlet = s;
    }
    
    public Tree_s create( final Tree_s parent, final String name, final int content ) {
        
        Tree_s tree = new Tree_s();
        
        if( tree != null ) {
            
            tree.m_subtrees = new Vector();
            if( tree.m_subtrees != null ) {
                // if NULL then is a ROOT (ex.SHELL ROOT))
                if( parent != null ) {
                    tree.m_parent = parent;
                    tree.m_name = name;
                    tree.m_content = content;
                    // adds new node to the parent's vector
                    parent.m_subtrees.addElement( tree );
                }
                return tree;
            }
        }
        return null;
    }
    
    /*
     *  Gets the parent node of the current node.
     *
     *  @param  tree    the current node
     *  @return a reference to the parent of the current node
     */
    public Tree_s getParent( final Tree_s tree ) {
        return tree.m_parent;
    }
    
    /*
     *  Gets the name of the current node.
     *
     *  @param  tree    the current node
     *  @return     the name of the current node
     */
    public String getName( final Tree_s tree ) {
        return tree.m_name;
    }
    
    /*
     *  Gets the content of the current node.
     *
     *  @param  tree    the current node
     *  @return     the content of the current node
     */
    public int getContent( final Tree_s tree ) {
        return tree.m_content;
    }
    
    /*
     *  Gets the subnode reference located at a given position inside the
     *  Subtrees vector of the current node.
     *  If current node hasn't childs (subnodes) or position is out of range
     *  then NULL will be returned.
     *
     *  @param  tree    current node to search for a subnode
     *  @param  pos     the position of the subnode you are requesting
     *
     *  @return the requested node at the given position or NULL if not found
     */
    public Tree_s getSubtreeAt( final Tree_s tree, final int pos ) {
        if( tree.m_subtrees != null ) {
            if( pos >= 0 && pos < tree.m_subtrees.size() ) {
                return (Tree_s)tree.m_subtrees.elementAt(pos);
            }
        }
        return null;
    }
    
    /*
     *  Gets the number of Subtrees (child nodes) contained in this node.
     *
     *  @param  tree    the current node
     *  @return     the number of child nodes
     */
    public int  getNumSubtrees( final Tree_s tree ) {
        int bResult = 0;
        if( tree.m_subtrees != null ) {
            bResult = tree.m_subtrees.size();
        }
        return bResult;
    }
    
    /*
     *  Navigates through the Tree structure by giving relative positions
     *  (either positive or negatives) from the current node.
     *  (ex. -1, -2, -3 steps back to the parent by one, two or three levels,
     *  0 returns the parent node and 1 returns itself.
     *
     *  @param  tree    the current node
     *  @param  pos     negative or positive values to navigate on
     *
     *  @return a reference to the new tree's node
     */
    public Tree_s tree( Tree_s tree, int pos ) {
        if( pos < 0 ) {
            pos--;
            while( pos != 0 && tree != null ) {
                tree = tree.m_parent;
                pos++;
            }
        } else {
            if( pos == 0 ) {
                tree = tree.m_parent;
            }
        }
        return tree;
    }
    
    /**
     *  Loads a file containing a valid Tree structure.
     *
     *  @param  tree        a root tree which will contain the tree structure
     *  @param  fname       filename contaning a tree structure in text format
     *
     *  @return True if succeed to load a Tree or False if failed.
     */
    public boolean load( final Tree_s tree, final String fname ) {
        m_TreeS = tree;
        m_CPOS = 0;
        
        // Initialize the Parser
        m_PT = new Parser( this, Utils.TOKEN, Utils.TOKEN_PIPE, 1, Parser.FORWARD );

        // Reads the shell.cfg file and creates the Tree structure
        FileStream file = null;
        file = (FileStream)file.create( m_Simlet, FileNames.FILE_SHEL, File.MODE_READ);
        if (file != null) {
            file.gets(this);
            if( !file.eof() ) {
                System.out.println("shell file error");
                file.close();
                return false;
            }
            file.close();
        }

//        //------------------------------
//        int len = mem_shell.length;                 // SOLO PARA PRUEBAS DESDE MEMORIA
//        for( int i=0; i<len; i++ ) {
//            byte[] m_aByte = mem_shell[i].getBytes();    // SOLO PARA PRUEBAS DESDE MEMORIA
//            m_PT.scan( m_aByte, 0, m_aByte.length );
//        }
//        //------------------------------
        
        return true;
    }

    /*
     *  A "callback" from fileGets
     */
    public boolean fileGetsLine(final byte[] line, int size, int crlf) {
        boolean ret = false;
        size = size - crlf;
        if( size > 0 ) {
            m_PT.scan( line, 0, size );
            ret = true;
        }
        return ret;
    }
    
    /*
     *  A "callback" from Parser
     */
    public boolean parserHit( final int hit, final byte[] line, int ofst, int len, Object something ) {
        boolean bRes = false;
        
        switch( hit )  {
            case 1:                     // gets the SubMenu level
                m_pos = Utils.memtoul(line, ofst, len, 10);
                bRes = true;
                break;
            case 2:                     // gets the Name
                m_strName = Utils.decoding(line, ofst, len, m_Simlet.getAppEncoding());
                bRes = true;
                break;
            case 3:                     // gets the Content
                m_Content = Utils.memtoul(line, ofst, len, 16);
                break;
        }
        return bRes;
    }
    
    /*
     * A "callback" from Parser
     * Ths method is automatically called after parser() has finished parsing
     * a line. A new node containing the extracted data by parserHit() will be
     * created and added to the Tree structure according to its sub-menu level
     * position. Sub-Menu levels are automatically calculated and positioned
     * by tree() methid and m_pos and m_CPOS variables.
     */
    public boolean parserEnd( boolean cancel ) {
        // Gets the node level where this new menu item should be stored
        m_TreeS = tree( m_TreeS, (m_pos - m_CPOS) );
        // Creates a new node
        m_TreeS = create( m_TreeS, m_strName, m_Content );
        // Remembers last menu level depth
        m_CPOS = m_pos;
	return true;
    }
    
    //-----------------------------------------
    // PRUEBAS DESDE MEMORIA
    //-----------------------------------------
//    private String mem_shell[] = {
//        "1|mem item 1-0|100",
//        "1|mem item 1-1|101",
//        "1|mem item 1-2|102",
//        "2|mem item 1-2-0|10200",
//        "2|mem item 1-2-1|10201",
//        "2|mem item 1-2-2|10202",
//        "2|mem item 1-2-3|10203",
//        "3|mem item 1-2-3-0|1020300",
//        "3|mem item 1-2-3-1|1020301",
//        "3|mem item 1-2-3-2|1020302",
//        "1|mem item 1-3|103",
//        "1|mem item 1-4|104",
//        "1|mem item 1-5|105",
//        "1|mem item 1-6|106",
//        "2|mem item 1-6-0|10601",
//        "2|mem item 1-6-1|10602",
//        "1|mem item 1-7|107",
//    };
    
}
